home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 April: Mac OS SDK / Dev.CD Apr 97 SDK1.toast / Development Kits (Disc 1) / Apple Game Sprockets / More Sprocket Examples 1.0 / GlyphaIV Sources / G4Sound.c < prev    next >
Encoding:
Text File  |  1996-06-12  |  13.7 KB  |  368 lines  |  [TEXT/CWIE]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Sound.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This file handles all sound routines.  It handles 2 concurrent sound…
  9. // channels allowing 2 sounds to be played simultaneously.  It also handles…
  10. // a system of priorites whereby you can ensure that "important" sounds don't…
  11. // get cut off by "lesser" sounds.  In that there are 2 channels however,…
  12. // "lesser" sounds are not discounted outright - both channels are considered…
  13. // to determine if one of the channels is not playing at all (priority = 0) or…
  14. // playing a sound of an even lesser priority.  Make sense?
  15.  
  16.  
  17. #include <Sound.h>
  18. #include "G4Externs.h"
  19. #include <Resources.h>
  20.  
  21. #define kMaxSounds                17            // Number of sounds to load.
  22. #define    kBaseBufferSoundID        1000        // ID of first sound (assumed sequential).
  23. #define kSoundDone                913            // Just a number I chose.
  24. #define kSoundDone2                749            // Just a number I chose.
  25.  
  26.  
  27. void PlaySound1 (short, short);
  28. void PlaySound2 (short, short);
  29. pascal void ExternalCallBack (SndChannelPtr, SndCommand *);
  30. pascal void ExternalCallBack2 (SndChannelPtr, SndCommand *);
  31. void LoadAllSounds (void);
  32. OSErr LoadBufferSounds (void);
  33. OSErr DumpBufferSounds (void);
  34. OSErr OpenSoundChannel (void);
  35. OSErr CloseSoundChannel (void);
  36.  
  37.  
  38. SndCallBackUPP        externalCallBackUPP, externalCallBackUPP2;
  39. SndChannelPtr        externalChannel, externalChannel2;
  40. Ptr                    theSoundData[kMaxSounds];
  41. short                externalPriority, externalPriority2;
  42. Boolean                channelOpen, soundOn;
  43.  
  44.  
  45. //==============================================================  Functions
  46. //--------------------------------------------------------------  PlaySound1
  47. // This function takes a sound ID and a priority, and forces that sound to 
  48. // play through channel 1 - and saves the priority globally.  As well, a 
  49. // callback command is queues up in channel 1.
  50.  
  51. void PlaySound1 (short soundID, short priority)
  52. {
  53.     SndCommand    theCommand;
  54.     OSErr        theErr;
  55.     
  56.     theCommand.cmd = flushCmd;            // Send 1st a flushCmd to clear the sound queue.
  57.     theCommand.param1 = 0;
  58.     theCommand.param2 = 0L;
  59.     theErr = SndDoImmediate(externalChannel, &theCommand);
  60.     
  61.     theCommand.cmd = quietCmd;            // Send quietCmd to stop any current sound.
  62.     theCommand.param1 = 0;
  63.     theCommand.param2 = 0L;
  64.     theErr = SndDoImmediate(externalChannel, &theCommand);
  65.     
  66.     externalPriority = priority;        // Copy priority to global variable.
  67.     
  68.     theCommand.cmd = bufferCmd;            // Then, send a bufferCmd to channel 1.
  69.     theCommand.param1 = 0;                // The sound played will be soundID.
  70.     theCommand.param2 = (long)(theSoundData[soundID]);
  71.     theErr = SndDoImmediate(externalChannel, &theCommand);
  72.     
  73.     theCommand.cmd = callBackCmd;        // Lastly, queue up a callBackCmd to notify us…
  74.     theCommand.param1 = kSoundDone;        // when the sound has finished playing.
  75.     theCommand.param2 = SetCurrentA5();
  76.     theErr = SndDoCommand(externalChannel, &theCommand, TRUE);
  77. }
  78.  
  79. //--------------------------------------------------------------  PlaySound2
  80. // This function is identical to the above function except that it handles…
  81. // playing sounds through channel 2.
  82.  
  83. void PlaySound2 (short soundID, short priority)
  84. {
  85.     SndCommand    theCommand;
  86.     OSErr        theErr;
  87.     
  88.     theCommand.cmd = flushCmd;            // Send 1st a flushCmd to clear the sound queue.
  89.     theCommand.param1 = 0;
  90.     theCommand.param2 = 0L;
  91.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  92.     
  93.     theCommand.cmd = quietCmd;            // Send quietCmd to stop any current sound.
  94.     theCommand.param1 = 0;
  95.     theCommand.param2 = 0L;
  96.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  97.     
  98.     externalPriority2 = priority;        // Copy priority to global variable.
  99.     
  100.     theCommand.cmd = bufferCmd;            // Then, send a bufferCmd to channel 1.
  101.     theCommand.param1 = 0;                // The sound played will be soundID.
  102.     theCommand.param2 = (long)(theSoundData[soundID]);
  103.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  104.     
  105.     theCommand.cmd = callBackCmd;        // Lastly, queue up a callBackCmd to notify us…
  106.     theCommand.param1 = kSoundDone2;    // when the sound has finished playing.
  107.     theCommand.param2 = SetCurrentA5();
  108.     theErr = SndDoCommand(externalChannel2, &theCommand, TRUE);
  109. }
  110.  
  111. //--------------------------------------------------------  PlayExternalSound
  112. // This function is probably poorly named for this application.  I lifted this…
  113. // whole library from one of my games and chopped it down for purposes of Glypha.
  114. // The original game treated "external" and "cockpit" sounds as seperate channels…
  115. // (such that cockpit sounds could only "override" other cockpit sounds and…
  116. // external sounds could only override other external sounds.
  117. // In any event, this is the primary function called from throughout Glypha.
  118. // This function is called with a sound ID and a priority (just some number) and…
  119. // the function then determines if one of the two sound channels is free to play…
  120. // the sound.  It determines this by way of priorities.  If a sound channel is…
  121. // idle and playing no sound, its channel priority is 0.  Since the priority of…
  122. // the sound you want to play is assumed to be greater than 0, it will, without…
  123. // a doubt, be allowed to play on an idle channel.  If however there is already…
  124. // a sound playing (the channel's priority is not equal to 0), the sound with the…
  125. // largest priority wins.  Mind you though that there are two channels to choose…
  126. // between.  Therefore, the function compares the priority passed in with the…
  127. // sound channel with the lowest priority.
  128.  
  129. void PlayExternalSound (short soundID, short priority)
  130. {                            // A little error-checking.
  131.     if ((soundID >= 0) && (soundID < kMaxSounds))
  132.     {
  133.         if (soundOn)        // More error-checking.
  134.         {                    // Find channel with lowest priority.
  135.             if (externalPriority < externalPriority2)
  136.             {                // Compare priority with that of channel 1.
  137.                 if (priority >= externalPriority)
  138.                     PlaySound1(soundID, priority);
  139.             }
  140.             else
  141.             {                // Compare priority with that of channel 2.
  142.                 if (priority >= externalPriority2)
  143.                     PlaySound2(soundID, priority);
  144.             }
  145.         }
  146.     }
  147. }
  148.  
  149. //--------------------------------------------------------  ExternalCallBack
  150. // Callback routine.  If this looks ugly, blame Apple's Universal Headers.
  151. // The callback routine is called after a sound finishes playing.  The…
  152. // callback routine is extremely useful in that it enables us to know when…
  153. // to set the sound channels priority back to 0 (meaning no sound playing).
  154. // Keep in mind (by the way) that this funciton is called at interrupt time…
  155. // and thus may not cause memory to be moved.  Also, note that also because…
  156. // of the interupt situation, we need to handle setting A5 to point to our…
  157. // app's A5 and then set it back again.
  158.  
  159. RoutineDescriptor ExternalCallBackRD = 
  160.         BUILD_ROUTINE_DESCRIPTOR(uppSndCallBackProcInfo, ExternalCallBack);
  161.  
  162. pascal void ExternalCallBack (SndChannelPtr theChannel, SndCommand *theCommand)
  163. {
  164.     long        thisA5, gameA5;
  165.     
  166.     if (theCommand->param1 == kSoundDone)    // See if it's OUR callback.
  167.     {
  168.         gameA5 = theCommand->param2;            // Extract our A5 from sound command.
  169.         thisA5 = SetA5(gameA5);                // Point A5 to our app (save off current A5).
  170.         
  171.         externalPriority = 0;                // Set global to reflect no sound playing.
  172.         
  173.         thisA5 = SetA5(thisA5);                // Restire A5.
  174.     }
  175. }
  176.  
  177. //--------------------------------------------------------  ExternalCallBack2
  178. // This function is identical to the above function but handles sound channel 2.
  179.  
  180. RoutineDescriptor ExternalCallBackRD2 = 
  181.         BUILD_ROUTINE_DESCRIPTOR(uppSndCallBackProcInfo, ExternalCallBack2);
  182.  
  183. pascal void ExternalCallBack2 (SndChannelPtr theChannel, SndCommand *theCommand)
  184. {
  185.     long        thisA5, gameA5;
  186.     
  187.     if (theCommand->param1 == kSoundDone2)    // See if it's OUR callback.
  188.     {
  189.         gameA5 = theCommand->param2;            // Extract our A5 from sound command.
  190.         thisA5 = SetA5(gameA5);                // Point A5 to our app (save off current A5).
  191.         
  192.         externalPriority2 = 0;                // Set global to reflect no sound playing.
  193.         
  194.         thisA5 = SetA5(thisA5);                // Restire A5.
  195.     }
  196. }
  197.  
  198. //--------------------------------------------------------  LoadBufferSounds
  199. // This function loads up all the sounds we'll need in the game and then…
  200. // strips off their header so that we can pass them as buffer commands.
  201. // Sounds are stored in our resource fork as 'snd ' resources.  There is a…
  202. // 20 byte header that we need to remove in order to use bufferCmd's.
  203. // This function is called only once, when the game loads up.
  204.  
  205. OSErr LoadBufferSounds (void)
  206. {
  207.     Handle        theSound;
  208.     long        soundDataSize;
  209.     OSErr        theErr;
  210.     short        i;
  211.     
  212.     theErr = noErr;                        // Assume no errors.
  213.     
  214.     for (i = 0; i < kMaxSounds; i++)    // Walk through all sounds.
  215.     {                                    // Load 'snd ' from resource.
  216.         theSound = GetResource('snd ', i + kBaseBufferSoundID);
  217.         if (theSound == 0L)                // Make sure it loaded okay.
  218.             return (ResError());        // Return reason it failed (if it did).
  219.         
  220.         HLock(theSound);                // If we got this far, lock sound down.
  221.                                         // Calculate size of sound minus header.
  222.         soundDataSize = GetHandleSize(theSound) - 20L;
  223.         HUnlock(theSound);                // Okay, unlock.
  224.                                         // Create pointer the size calculated above.
  225.         theSoundData[i] = NewPtr(soundDataSize);
  226.         if (theSoundData[i] == 0L)        // See if we created it okay.
  227.             return (MemError());        // If failed, return the reason why.
  228.         HLock(theSound);                // Okay, lock the sound handle again.
  229.                                         // Copy sound data (minus header) to our pointer.
  230.         BlockMove((Ptr)(*theSound + 20L), theSoundData[i], soundDataSize);
  231.         HUnlock(theSound);                // Unlock sound handle again.
  232.         ReleaseResource(theSound);        // And toss it from memory.
  233.     }
  234.     
  235.     return (theErr);
  236. }
  237.  
  238. //--------------------------------------------------------  DumpBufferSounds
  239. // This function is called when Glypha exits (quits).  All those nasty pointers…
  240. // we created in the above function are reclaimed.
  241.  
  242. OSErr DumpBufferSounds (void)
  243. {
  244.     OSErr        theErr;
  245.     short        i;
  246.     
  247.     theErr = noErr;
  248.     
  249.     for (i = 0; i < kMaxSounds; i++)        // Go through all sound pointers.
  250.     {
  251.         if (theSoundData[i] != 0L)            // Make sure it exists.
  252.             DisposPtr(theSoundData[i]);        // Dispose of it.
  253.         theSoundData[i] = 0L;                // Make sure it reflects its "nonexistence".
  254.     }
  255.     
  256.     return (theErr);
  257. }
  258.  
  259. //--------------------------------------------------------  OpenSoundChannel
  260. // This should perhaps be called OpenSoundChannels() since it opens two.
  261. // It is called once (at initialization) to set up the two sound channels…
  262. // we will use throughout Glypha.  For purposes of speed, 8-bit sound channels…
  263. // with no interpolation and monophonic are opened.  They'll use the sampled…
  264. // synthesizer (digitized sound) and be assigned their respective callback…
  265. // routines.
  266.  
  267. OSErr OpenSoundChannel (void)
  268. {
  269.     OSErr        theErr;
  270.     
  271.     #if USESROUTINEDESCRIPTORS
  272.         externalCallBackUPP = &ExternalCallBackRD;    // Handle Universal Header ugliness.
  273.         externalCallBackUPP2 = &ExternalCallBackRD2;
  274.     #else
  275.         externalCallBackUPP = (SndCallBackUPP) &ExternalCallBack;
  276.         externalCallBackUPP2 = (SndCallBackUPP) &ExternalCallBack2;
  277.     #endif
  278.     
  279.     theErr = noErr;                                    // Assume no errors.
  280.     
  281.     if (channelOpen)                                // Error checking.
  282.         return (theErr);
  283.     
  284.     externalChannel = 0L;
  285.     theErr = SndNewChannel(&externalChannel,         // Open channel 1.
  286.             sampledSynth, initNoInterp + initMono, 
  287.             (SndCallBackUPP)externalCallBackUPP);
  288.     if (theErr == noErr)                            // See if it worked.
  289.         channelOpen = TRUE;
  290.     
  291.     externalChannel2 = 0L;
  292.     theErr = SndNewChannel(&externalChannel2,         // Open channel 2.
  293.             sampledSynth, initNoInterp + initMono, 
  294.             (SndCallBackUPP)externalCallBackUPP2);
  295.     if (theErr == noErr)                            // See if it worked.
  296.         channelOpen = TRUE;
  297.     
  298.     return (theErr);
  299. }
  300.  
  301. //--------------------------------------------------------  CloseSoundChannel
  302. // This function is called only upon quitting Glypha.  Both sound channels…
  303. // we created above are closed down.
  304.  
  305. OSErr CloseSoundChannel (void)
  306. {
  307.     OSErr        theErr;
  308.     
  309.     theErr = noErr;
  310.     
  311.     if (!channelOpen)            // Error checking.
  312.         return (theErr);
  313.     
  314.     if (externalChannel != 0L)    // Dispose of channel 1 (if open).
  315.         theErr = SndDisposeChannel(externalChannel, TRUE);
  316.     externalChannel = 0L;        // Flag it closed.
  317.     
  318.     if (externalChannel2 != 0L)    // Dispose of channel 2 (if open).
  319.         theErr = SndDisposeChannel(externalChannel2, TRUE);
  320.     externalChannel2 = 0L;        // Flag it closed.
  321.     
  322.     if (theErr == noErr)
  323.         channelOpen = FALSE;
  324.     
  325.     return (theErr);
  326. }
  327.  
  328. //--------------------------------------------------------  InitSound
  329. // All the above initialization routines are handled by this one function.
  330. // This single function is the only one that needs to be called - it handles…
  331. // calling the functions that load the sounds and create the sound channels.
  332. // It is called from main() when Glypha is loading up and going through its…
  333. // initialization phase.
  334.  
  335. void InitSound (void)
  336. {
  337.     OSErr        theErr;
  338.     
  339.     soundOn = TRUE;            // Note that initialization of sounds has occurred…
  340.                             // (or rather is just about to this instant!).
  341.     externalChannel = 0L;    // Flag channels as nonexistant.
  342.     externalChannel2 = 0L;
  343.     externalPriority = 0;    // Set priorities to 0 (no sound playing).
  344.     externalPriority2 = 0;
  345.                             // Load up all sounds (see above function).
  346.     theErr = LoadBufferSounds();
  347.     if (theErr != noErr)    // If it fails, we'll quit Glypha.
  348.         RedAlert("\pFailed Loading Sounds");
  349.                             // Open up the two sound channels.
  350.     theErr = OpenSoundChannel();
  351.     if (theErr != noErr)    // If that fails we'll quit Glypha as well.
  352.         RedAlert("\pFailed To Open Sound Channels");
  353. }
  354.  
  355. //--------------------------------------------------------  KillSound
  356. // Complementary to the above function, this one is called only when Glypha…
  357. // quits and it handles all the "shut-down" routines.  It also is called from…
  358. // main(), but it is called last - just as Glypha is quitting.
  359.  
  360. void KillSound (void)
  361. {
  362.     OSErr        theErr;
  363.     
  364.     theErr = DumpBufferSounds();    // Kill all sound pointers.
  365.     theErr = CloseSoundChannel();    // Close down the sound channels.
  366. }
  367.  
  368.